package com.oldroid.apps.getit.provider;

import java.util.Arrays;
import java.util.HashMap;

import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;

import com.oldroid.apps.getit.provider.DownloadsContract.Bookmarks;
import com.oldroid.apps.getit.provider.DownloadsContract.Downloads;
import com.oldroid.apps.getit.provider.DownloadsContract.Downloads_Tags;
import com.oldroid.apps.getit.provider.DownloadsContract.Tags;
import com.oldroid.apps.getit.provider.DownloadsDatabase.Tables;
import com.oldroid.apps.getit.util.SelectionBuilder;

public class DownloadsProvider extends ContentProvider {
	private static final String TAG = "DownloadsProvider";
	private static final boolean LOGV = false;

	private DownloadsDatabase mOpenHelper;

	private static final UriMatcher sUriMatcher;

	private static final int DOWNLOADS = 100;
	private static final int DOWNLAOD_ID = 101;
	private static final int DOWNLODS_STARRED = 102;

	private static final int TAGS = 200;
	private static final int TAG_ID = 201;

	private static final int DOWNLOADS_TAGS = 300;
	private static final int DOWNLOADS_TAG_ID = 301;

	private static final int BOOKMARKS = 400;
	private static final int BOOKMARKS_ID = 401;

	private static final int SEARCH = 800;
	private static final int SEARCH_SUGGEST = 801;
	private static final int SEARCH_TAGS = 802;
	private static final int SEARCH_TAGS_SUGGEST = 803;

	private static HashMap<String, String> sDownloadsProjectionMap;
	private static HashMap<String, String> sTagsProjectionMap;
	private static HashMap<String, String> sDownloads_TagsProjectionMap;
	private static HashMap<String, String> sBookmarksProjectionMap;

	/**
	 * Build and return a {@link UriMatcher} that catches all {@link Uri}
	 * variations supported by this {@link ContentProvider}.
	 */
	static {
		sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		final String authority = DownloadsContract.CONTENT_AUTHORITY;

		sUriMatcher.addURI(authority, "downloads", DOWNLOADS);
		sUriMatcher.addURI(authority, "downloads/#", DOWNLAOD_ID);
		sUriMatcher.addURI(authority, "downloads/starred/", DOWNLODS_STARRED);

		sUriMatcher.addURI(authority, "tags", TAGS);
		sUriMatcher.addURI(authority, "tags/#", TAG_ID);

		sUriMatcher.addURI(authority, "downloads_tags", DOWNLOADS_TAGS);
		sUriMatcher.addURI(authority, "downloads_tags/#", DOWNLOADS_TAG_ID);

		sUriMatcher.addURI(authority, "bookmarks", BOOKMARKS);
		sUriMatcher.addURI(authority, "bookmarks/#", BOOKMARKS_ID);

		sUriMatcher.addURI(authority, "search", SEARCH);
		sUriMatcher.addURI(authority, "search_suggest_query", SEARCH_SUGGEST);

		sUriMatcher.addURI(authority, "search_tags", SEARCH_TAGS);
		sUriMatcher.addURI(authority, "search_tags_suggest_query", SEARCH_TAGS_SUGGEST);

		sDownloadsProjectionMap = new HashMap<String, String>();
		sDownloadsProjectionMap.put(Downloads._ID, Downloads._ID);
		sDownloadsProjectionMap.put(Downloads.FILENAME, Downloads.FILENAME);
		sDownloadsProjectionMap.put(Downloads.URI, Downloads.URI);
		sDownloadsProjectionMap.put(Downloads.CREATED, Downloads.CREATED);
		sDownloadsProjectionMap.put(Downloads.MODIFIED, Downloads.MODIFIED);
		sDownloadsProjectionMap.put(Downloads.TOTAL_BYTES, Downloads.TOTAL_BYTES);
		sDownloadsProjectionMap.put(Downloads.DELETED, Downloads.DELETED);

		sTagsProjectionMap = new HashMap<String, String>();
		sTagsProjectionMap.put(Tags._ID, Tags._ID);
		sTagsProjectionMap.put(Tags.TAG, Tags.TAG);

		sDownloads_TagsProjectionMap = new HashMap<String, String>();
		sDownloads_TagsProjectionMap.put(Downloads_Tags._ID, Downloads_Tags._ID);
		sDownloads_TagsProjectionMap.put(Downloads_Tags.TAG_ID, Downloads_Tags.TAG_ID);
		sDownloads_TagsProjectionMap.put(Downloads_Tags.DOWNLOADS_ID, Downloads_Tags.DOWNLOADS_ID);

		sBookmarksProjectionMap = new HashMap<String, String>();
		sBookmarksProjectionMap.put(Bookmarks._ID, Bookmarks._ID);
		sBookmarksProjectionMap.put(Bookmarks.FILELOCATION, Bookmarks.FILELOCATION);
		sBookmarksProjectionMap.put(Bookmarks.NAME, Bookmarks.NAME);
	}

	@Override
	public boolean onCreate() {
		final Context context = getContext();
		mOpenHelper = new DownloadsDatabase(context);
		return true;
	}

	@Override
	public String getType(Uri uri) {
		final int match = sUriMatcher.match(uri);
		switch (match) {
		case DOWNLODS_STARRED:
		case DOWNLOADS:
			return Downloads.CONTENT_TYPE;
		case DOWNLAOD_ID:
			return Downloads.CONTENT_ITEM_TYPE;
		case TAGS:
			return Tags.CONTENT_TYPE;
		case TAG_ID:
			return Tags.CONTENT_ITEM_TYPE;
		case DOWNLOADS_TAGS:
			return Downloads_Tags.CONTENT_TYPE;
		case DOWNLOADS_TAG_ID:
			return Downloads_Tags.CONTENT_ITEM_TYPE;
		case BOOKMARKS:
			return Bookmarks.CONTENT_TYPE;
		case BOOKMARKS_ID:
			return Bookmarks.CONTENT_ITEM_TYPE;
		default:
			throw new UnsupportedOperationException("Can't Get Type. Unknown uri: " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		if (LOGV)
			Log.v(TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
		final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
		final int match = sUriMatcher.match(uri);
		long rowId = -1;
		switch (match) {
		case DOWNLOADS:
			rowId = db.insertOrThrow(Tables.DOWNLOADS, null, values);
			getContext().getContentResolver().notifyChange(uri, null);
			return Downloads.buildDownloadsUri(rowId);
		case TAGS:
			rowId = db.insertWithOnConflict(Tables.TAGS, null, values, SQLiteDatabase.CONFLICT_IGNORE);
			Log.v(TAG, "rowID=" + rowId);
			if (rowId < 0) {
				Log.v(TAG, values.getAsString(Tags.TAG));
				Cursor c = db.query(Tables.TAGS, null, null, null, null, null, null);
				Log.v("TAG", "" + c.getCount());
				c.moveToNext();
				Log.v("TAG", "" + c.getInt(0));
			}
			getContext().getContentResolver().notifyChange(uri, null);
			return Tags.buildTagsUri(rowId);
		case DOWNLOADS_TAGS:
			rowId = db.insertOrThrow(Tables.DOWNLOADS_TAGS, null, values);
			getContext().getContentResolver().notifyChange(uri, null);
			return Downloads_Tags.buildDownloads_TagsUri(rowId);
		case BOOKMARKS:
			rowId = db.insertOrThrow(Tables.BOOKMARKS, null, values);
			getContext().getContentResolver().notifyChange(uri, null);
			return Bookmarks.buildBookmarksUri(rowId);
		default:
			throw new UnsupportedOperationException("Can't Insert. Unknown uri: " + uri);
		}
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		if (LOGV)
			Log.v(TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
		final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
		final SelectionBuilder builder = buildSimpleSelection(uri);
		int retVal = builder.where(selection, selectionArgs).update(db, values);
		getContext().getContentResolver().notifyChange(uri, null);
		return retVal;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		if (LOGV)
			Log.v(TAG, "delete(uri=" + uri + ")");
		final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
		final SelectionBuilder builder = buildSimpleSelection(uri);
		int retVal = builder.where(selection, selectionArgs).delete(db);
		getContext().getContentResolver().notifyChange(uri, null);
		return retVal;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		if (LOGV)
			Log.v(TAG,
					"query(uri=" + uri + ", selection=" + selection + ", selectionArgs="
							+ Arrays.toString(selectionArgs));
		final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
		String query = uri.getLastPathSegment().toLowerCase();
		final int match = sUriMatcher.match(uri);
		switch (match) {
		default: {
			final SelectionBuilder builder = buildSimpleSelection(uri);
			return builder.where(selection, selectionArgs).query(db, projection, sortOrder);
		}
		// will implement search and search suggestion features once the
		// download fragment works fine.
		case DOWNLODS_STARRED:
			return db.query(Tables.DOWNLOADS, projection, selection, selectionArgs, Downloads.FILENAME, "" + Downloads.STARRED + ">0",
					sortOrder);

		case SEARCH_SUGGEST:
			/*
			 * final SelectionBuilder builder = new SelectionBuilder();
			 * selectionArgs[0] = selectionArgs[0] + "%";
			 * builder.table(Tables.DOWNLOADS); builder.where(selection,
			 * selectionArgs); builder.map(Downloads.FILENAME,
			 * SearchManager.SUGGEST_COLUMN_TEXT_1);
			 */

			// return null;//

			String[] searchSuggestTable = { "_ID", SearchManager.SUGGEST_COLUMN_TEXT_1 };
			MatrixCursor matrixCursor = new MatrixCursor(searchSuggestTable);
			//
			matrixCursor.addRow(new String[] { "0", "Test-Value 1" });
			matrixCursor.addRow(new String[] { "1", "Test-Value 2" });
			matrixCursor.addRow(new String[] { "2", "Test-Value 3" });
			// return matrixCursor;

			return searchSuggest(uri, projection, selection, selectionArgs, sortOrder);
		}

	}

	private void say(String msg) {
		Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
	}

	private void log(String msg) {
		Log.e("DownloadsProvider", msg);
	}

	private Cursor searchSuggest(Uri uri, String[] projection, String selection, String[] selectionArgs,
			String sortOrder) {

		// check if query is empty
		if (selectionArgs[0].equals("")) {
			return null;
		}

		// get the query
		String query = selectionArgs[0];

		ContentResolver mURI = this.getContext().getContentResolver();
		String[] myProjection = null; // get all the columns
		String mySelection = Downloads.FILENAME + " LIKE '%" + query + "%'"; // Like
																				// statemnts
																				// tells
																				// SQL
																				// to
																				// do
																				// partial
																				// matching
		String[] mySelectionArgs = null;
		String mySortOrder = Downloads.FILENAME + " ASC"; // ascending order

		Cursor result = mURI.query(Downloads.CONTENT_URI, myProjection, mySelection, mySelectionArgs, mySortOrder);

		// say("Search suggest has " + result.getCount() + " results");
		log("Search suggest has " + result.getCount() + " results");

		String[] searchSuggestTable = { "_ID", SearchManager.SUGGEST_COLUMN_TEXT_1,
				SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID };
		MatrixCursor matrixCursor = new MatrixCursor(searchSuggestTable);

		int count = 0;

		// Enumerate through results, and add them to the Matrix Cursor.
		// This is because for the suggestions to work the cursor return must
		// have
		// in the column names "_ID" and "SUGGEST_COLUMN_TEXT_1"
		while (result.moveToNext()) {

			String filename = result.getString(result.getColumnIndex(Downloads.FILENAME));
			// filename = filename.toLowerCase(); // convert to lowercase
			int index_id = result.getInt(result.getColumnIndex(Downloads._ID));

			String[] row = { String.valueOf(count), filename, String.valueOf(index_id) };
			matrixCursor.addRow(row);
			count++;
		}

		return matrixCursor;
	}

	private SelectionBuilder buildSimpleSelection(Uri uri) {
		final SelectionBuilder builder = new SelectionBuilder();
		final int match = sUriMatcher.match(uri);
		switch (match) {
		case DOWNLOADS:
			return builder.table(Tables.DOWNLOADS);
		case DOWNLAOD_ID:
			return builder.table(Tables.DOWNLOADS).where(Downloads._ID + "=?", uri.getPathSegments().get(1));
		case TAGS:
			return builder.table(Tables.TAGS);
		case TAG_ID:
			return builder.table(Tables.TAGS).where(Tags._ID + "=?", uri.getPathSegments().get(1));
		case DOWNLOADS_TAGS:
			return builder.table(Tables.DOWNLOADS_TAGS);
		case DOWNLOADS_TAG_ID:
			return builder.table(Tables.DOWNLOADS_TAGS).where(Downloads_Tags._ID + "=?", uri.getPathSegments().get(1));
		case BOOKMARKS:
			return builder.table(Tables.BOOKMARKS);
		case BOOKMARKS_ID:
			return builder.table(Tables.BOOKMARKS).where(Bookmarks._ID + "=?", uri.getLastPathSegment());

		case SEARCH:
			return builder.table(Tables.DOWNLOADS).mapToTable(Downloads._ID, Tables.DOWNLOADS)
					.mapToTable(Downloads.FILENAME, Tables.DOWNLOADS);
		case SEARCH_TAGS:
			final String query = uri.getPathSegments().get(2);
			return builder.table(Tables.TAGS).mapToTable(Tags._ID, Tables.TAGS).mapToTable(Tags.TAG, Tables.TAGS)
					.where(Tags.TAG + " MATCH ?", query);
		default:
			throw new UnsupportedOperationException("Unknown uri: " + uri);
		}
	}

}
